/**
 * @Created Mar 29, 2011 3:46:08 PM
 * @author cry30
 */
package com.philip.journal.core.dao.spring.hibernate;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;

import com.philip.core.WarningType;
import com.philip.journal.common.BeanUtils;
import com.philip.journal.core.exception.JournalException;

/**
 * Refactored out from {@link BaseDAOSpringHibernate#readObject(String, Map)}.
 *
 * @param <E> element type.
 */
class ReadObjectCallback0<E> implements HibernateCallback<E> {

    /** Class logger. */
    private final Logger logger = Logger.getLogger(ReadObjectCallback0.class); // NOPMD by r39

    /** Instance reference to named query. */
    private final transient String namedQuery;

    /** Instance reference to parameter map. */
    private final transient Map<String, Object> map;

    /**
     * Default constructor.
     *
     * @param pNamedQuery named query.
     * @param pMap parameter map.
     */
    ReadObjectCallback0(final String pNamedQuery, final Map<String, Object> pMap) {
        this.namedQuery = pNamedQuery;
        this.map = pMap;
    }


    /**
     * Performs query inside the scope of hibernate.
     *
     * @param session hibernate session.
     * @return the result of the method call inside hibernate scope.
     */
    @SuppressWarnings(WarningType.UNCHECKED)
    @Override
    public E doInHibernate(final Session session)
    {
        session.enableFilter("patchFilter");
        final Query query = session.getNamedQuery(namedQuery);

        final Class<?>[] clsArrParam = new Class[] {
                String.class,
                null };
        final Object[] objArrParam = new Object[2];
        for (final String key : map.keySet()) {
            final Object obj = map.get(key);
            final Class<?> type = obj == null ? Object.class : obj.getClass();

            try {
                if (type.equals(Object.class)) {
                    query.setParameter(key, obj);
                } else {
                    clsArrParam[1] = BeanUtils.getPrimitive(type);
                    objArrParam[0] = key;
                    objArrParam[1] = obj;
                    Query.class.getMethod("set" + type.getSimpleName(), clsArrParam).invoke(query, objArrParam);
                }
            } catch (final InvocationTargetException ite) {
                throw JournalException.wrapperException(new IllegalArgumentException("Invalid parameter name[" + key
                        + "].", ite));
            } catch (final NoSuchMethodException nsme) {
                throw JournalException.wrapperException(new IllegalArgumentException("Invalid parameter value type["
                        + obj + "]", nsme));
            } catch (final Exception e) {
                throw new JournalException("System Error [" + key + "," + obj + "]", e);
            }
        }
        E retval = null;
        try {
            retval = (E) query.uniqueResult();
        } catch (final Exception e) {
            logger.trace(e.getMessage(), e);
        }
        return retval;
    }
}
